﻿using System;
using System.Collections.Generic;
using System.Threading; 
using System.Text;
using TekaMUD.Engine.Commands;
using TekaMUD.Engine.Blocks;
using TekaMUD.Engine.Framework;

namespace TekaMUD.Engine.IO
{
	public abstract class BaseChannel : IDisposable
	{
		private Stack<CommandHandler> _commandStack;
		private Stack<string> _helperStringStack;
		private Player _player;
		private volatile ChannelStatusEnum _status;
		public bool PromptEnabled { get; set; }
		private Dictionary<Thread, ChannelOutputHolder> _outputHolders;
		private object SyncRoot = new object();

		public BaseChannel()
		{
			_commandStack = new Stack<CommandHandler>();
			_helperStringStack = new Stack<string>();
			_outputHolders = new Dictionary<Thread, ChannelOutputHolder>();
		}

		public ChannelStatusEnum Status
		{
			get { return _status; }
			protected set { _status = value; }
		}

		public Player Player
		{
			get
			{
				return _player;
			}
			protected set
			{
				_player = value;
			}
		}

		public void RegisterOutputHolder(ChannelOutputHolder coh)
		{
			lock (SyncRoot)
			{
				Thread currentThread = Thread.CurrentThread;

				if (_outputHolders.ContainsKey(currentThread))
					return;

				_outputHolders[currentThread] = coh;
			}
		}

		protected ChannelOutputHolder CurrentOutputHolder
		{
			get
			{
				lock (SyncRoot)
				{
					Thread currentThread = Thread.CurrentThread;

					if (!_outputHolders.ContainsKey(currentThread))
						return null;
					else
						return _outputHolders[currentThread];
				}
			}
		}

		/// <summary>
		/// Called when the ChannelOutputHolder for current thread is disposed.
		/// </summary>
		public void Flush(ChannelOutputHolder coh)
		{
			lock (SyncRoot)
			{
				Thread currentThread = Thread.CurrentThread;

				if(_outputHolders.ContainsKey(currentThread))
					if (_outputHolders[currentThread] == coh)
					{
						FlushHolderToStream(coh);

						_outputHolders.Remove(currentThread);
					}
			}
		}

		protected abstract void FlushHolderToStream(ChannelOutputHolder coh);

		#region Stacks

		public void PushNextHandler(CommandHandler commandHandler)
		{
            Logger.LogDebug("Pushing handler: " + commandHandler.Method.Name);

			_commandStack.Push(commandHandler);
		}

		public CommandHandler PopNextHandler()
		{
            Logger.LogDebug("poping handler: " + (_commandStack.Count > 0 ? _commandStack.Peek().Method.Name : "empty!"));

			if (_commandStack.Count > 0)
				return _commandStack.Pop();
			else
				return null;
		}

		public void PushHelperString(string s)
		{
			_helperStringStack.Push(s);
		}

		public string PopHelperString()
		{
			if (_helperStringStack.Count > 0)
				return _helperStringStack.Pop();
			else
				return null;
		}

		#endregion

		#region Write methods

		public void WriteLine()
		{
			WriteString("\r\n");
		}

		public abstract void WriteString(string s);
		public abstract void Close();
		public abstract void WriteLine(string s, AnsiColors fgColor, AnsiColors? bgColor, bool brightFG, bool brightBG);
		public abstract void WriteString(string s, AnsiColors fgColor, AnsiColors? bgColor, bool brightFG, bool brightBG);
		public abstract void SendPrompt();

		#endregion

		public void AssignPlayer(Player p)
		{
			_player = p;
		}

		public void Dispose()
		{
			throw new NotImplementedException();
		}

		internal void ReleaseChannel()
		{
			throw new NotImplementedException();
		}
	}
}
